package cn.schoolwow.quickdao.module.database.transaction.kit;

import cn.schoolwow.quickdao.domain.QuickDAOConfig;
import cn.schoolwow.quickdao.domain.database.dml.DatabaseManipulation;
import cn.schoolwow.quickdao.domain.exception.SQLRuntimeException;
import cn.schoolwow.quickdao.domain.provider.DatabaseProvider;
import cn.schoolwow.quickdao.module.common.operation.kit.CommonDatabaseOption;
import cn.schoolwow.quickdao.module.common.operation.kit.CommonDatabaseOptionImpl;
import cn.schoolwow.quickdao.module.common.query.kit.QueryOperation;
import cn.schoolwow.quickdao.module.common.query.kit.QueryOperationImpl;
import cn.schoolwow.quickdao.module.database.ddl.kit.DatabaseDefinition;
import cn.schoolwow.quickdao.module.database.ddl.kit.DatabaseDefinitionInvocationHandler;
import cn.schoolwow.quickdao.module.database.dml.common.domain.ManipulationOption;
import cn.schoolwow.quickdao.module.database.dml.kit.DatabaseManipulationInvocationHandler;
import cn.schoolwow.quickflow.QuickFlow;
import cn.schoolwow.quickflow.QuickFlowBuilder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 事务操作代理模式对象
 */
public class TransactionInvocationHandler implements InvocationHandler {
    private Connection transactionConnection;
    private QuickFlow quickFlow;
    private QueryOperation queryOperation;
    private CommonDatabaseOption commonDatabaseOption;
    private TransactionOperation transactionOperation;

    public TransactionInvocationHandler(QuickFlow quickFlow) {
        try {
            this.quickFlow = setTransactionConnection(quickFlow);
            this.queryOperation = new QueryOperationImpl(quickFlow);
            this.commonDatabaseOption = new CommonDatabaseOptionImpl(quickFlow);
            this.transactionOperation = new TransactionOperationImpl(transactionConnection);
        }catch (SQLException e){
            throw new SQLRuntimeException(e);
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String interfaceName = method.getDeclaringClass().getSimpleName();
        switch (interfaceName) {
            case "Closeable":
            case "TransactionOperation": {
                return method.invoke(transactionOperation, args);
            }
            case "Object":
            case "DatabaseDAO":
            case "DatabaseManipulationParent":
            case "DatabaseManipulationOption":
            case "DatabaseManipulationInstance":
            case "DatabaseManipulationJSON":{
                DatabaseManipulation databaseManipulation = (DatabaseManipulation) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                        new Class<?>[]{DatabaseManipulation.class}, new DatabaseManipulationInvocationHandler(quickFlow));
                ManipulationOption manipulationOption = quickFlow.getContextThreadLocalInstanceData(ManipulationOption.class);
                manipulationOption.databaseManipulation = databaseManipulation;
                return method.invoke(databaseManipulation, args);
            }
            case "QueryOperation":{
                return method.invoke(queryOperation, args);
            }
            case "DatabaseDefinitionQuery":
            case "DatabaseDefinitionAction":{
                DatabaseDefinition databaseDefinition = (DatabaseDefinition) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                        new Class<?>[]{DatabaseDefinition.class}, new DatabaseDefinitionInvocationHandler(quickFlow));
                return method.invoke(databaseDefinition, args);
            }
            case "CommonDatabaseOption":{
                return method.invoke(commonDatabaseOption, args);
            }
            default: {
                throw new IllegalArgumentException("不支持的方法调用!接口名:"+interfaceName+",方法:" + method);
            }
        }
    }

    private QuickFlow setTransactionConnection(QuickFlow quickFlow) throws SQLException {
        QuickDAOConfig quickDAOConfig = quickFlow.getContextInstanceData(QuickDAOConfig.class);
        transactionConnection = quickDAOConfig.databaseContext.dataSource.getConnection();
        transactionConnection.setAutoCommit(false);

        QuickFlow transactionQuickFlow = QuickFlowBuilder.newInstance()
                .printTrace(quickDAOConfig.logRecordOption.recordFlowLog)
                .putContextData("transactionConnection", transactionConnection)
                .putContextTemporaryInstanceData(quickDAOConfig)
                .putContextTemporaryInstanceData(quickDAOConfig.databaseContext.databaseProvider.getDatabaseType())
                .putContextTemporaryInstanceData(quickDAOConfig.databaseContext.databaseProvider, DatabaseProvider.class);
        return transactionQuickFlow;
    }
}
